/**
 * Copyright 2002 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys .NET Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 //@Author	: Lejil 
//@Class	: MetaData

#include "stdafx.h"
#include "MetaData.h" 

// Lejil
// 11/08/04 
// If the method arguments are ByRef Type, that needs to be generated as
//	out parameter in xsd



//  Get All Classes in  the .NET Assembly
//       First Load the .NET Assembly using reflection , then get all
//       the classes in that Assembly
//
int MetaData::GetAllClassesAndNamespace(String * assemblyName , String *nameSpace)
{
   Assembly  * assembly			= NULL; 
   Type __gc * classInfo[]		= NULL;
   int assemblyNode				= NULL;
   try
   {
		assembly			=	LoadAssembly(assemblyName);
		assemblyNode		=	document->CreateElement("assembly");
		document->CreateTextElementWithParentNS("name",assembly->FullName,assemblyNode);
		int classesNode		=	document->CreateElementWithParentNS("classes",assemblyNode);
		classInfo			=	assembly->GetTypes();
		//Filter the name spaces seperatly from the class name
		// For more readability and ease of use
		ArrayList  *namespaceList =	new ArrayList();
		
		for (int i=0; i<classInfo.Length; i++)
		{
			 String	  *	className	= classInfo[i]->FullName; 
			
			 // check ,whether default constructor present or Not
			 ConstructorInfo  *constrInfo =	classInfo[i]->GetConstructor(Type::EmptyTypes);
			 if(constrInfo == NULL )
				 continue;
			 if (className->StartsWith(nameSpace) )
			 {
				int endPosition		= className->IndexOf(nameSpace)+ nameSpace->Length;
				int namespaceLength	= className->Length - endPosition;

				String   * namespaceVal = className->Substring(endPosition,namespaceLength);
				int nextPos			    = namespaceVal->IndexOf(".");
				
				if(nextPos > 0)
				{
					String *clsName= className->Substring(endPosition,nextPos+1);
					//CHECK THE ITEM IS  ADDED OR NOT
					if ( !namespaceList->Contains(clsName)) 
					{
						CreateClassNode(classesNode,clsName);
						namespaceList->Add(clsName);
					}
					clsName = NULL;
				}
				else
				{
					//CHECK THE ITEM IS  ADDED OR NOT
					if (! namespaceList->Contains(namespaceVal))
					{
						 CreateClassNode(classesNode,namespaceVal);
						 namespaceList->Add(namespaceVal);
					}
				}
			 }
		}
		namespaceList= NULL;
   }
   catch(Exception *ex)
   {
		throw ex;
   }
   __finally
   {
	   if (classInfo != NULL)
			delete[] classInfo;	
	   assembly = NULL;
   }
   return  assemblyNode;
   
}


//
// Get All the  Methods in the .NET Class
//   //<arguments are AssemblyName,Class Name, static = true or false>  
//   First Load the Assembly,Then get the specified class.
//     IF the Argument IsStatic is True 
//            Get Only The Public Static Methods
//     ELSE
//			  Get All the public Methods
//
int MetaData::GetAllMethods(String* assemblyName,String *className,bool IsStatic)
{
   Assembly  * assembly =NULL;
   Type __gc * clazz	=NULL;
   int classNode =0;
   try
   {
		assembly   =  LoadAssembly(assemblyName);
		classNode  =  document->CreateElement("class");
		clazz	   =  assembly->GetType(className);
		
		int methodsNode=document->CreateElementWithParentNS("methods",classNode);
				
		//GET All the methods in this class
		MethodInfo __gc *methodInfo[] =clazz->GetMethods();
		
		
		ParameterInfo __gc *paramInfo[]= NULL;
		for(int j=0; j< methodInfo.Length;	j++)
		{
			if (IsStatic)
			{
				if (methodInfo[j]->IsStatic)
				{
					paramInfo = methodInfo[j]->GetParameters();
					//create methodNode for all the static methods in the class
					CreateMethodNode(methodsNode,methodInfo[j],paramInfo);
				}
			}
			else
			{
				paramInfo = methodInfo[j]->GetParameters();
				//create methodNode for all the methods in the class
				CreateMethodNode(methodsNode,methodInfo[j],paramInfo);
			}
		}
		if (paramInfo != NULL)
				delete[] paramInfo;
		if (methodInfo != NULL)
				delete[] methodInfo;
   }
   catch(Exception *ex)
   {
		throw ex;
   }
   __finally
   {
		assembly =NULL;
		clazz	=NULL;
   }
 
   return  classNode;
}


//  Creates the Class Node
//
//
int MetaData::CreateClassNode(int classesNode,String *className)
{
	try
	{
		int classNode = document->CreateElementWithParentNS("class",classesNode);
		document->CreateTextElementWithParentNS("name",className,classNode);
		return classNode;
	}
	catch(Exception *ex)
	{
		throw ex;
	}
}

//
// Creates The Method Node
//
//
void MetaData::CreateMethodNode(int classNode,MethodInfo *method,ParameterInfo *paramInfo[])
{
	try
	{
		int methodNode		=   document->CreateElementWithParentNS("method",classNode);
		String * isStatic   =	method->IsStatic.ToString()->ToLower();
		Node::SetAttribute(methodNode,"static",isStatic);
		document->CreateTextElementWithParentNS("signature",method->ToString(),methodNode);
		document->CreateTextElementWithParentNS("name",method->Name,methodNode);
		
		Type * returnType   =	method->ReturnType;
		String * defaultValue = "0";
		String *mtdReturnType=	ConvertType(returnType,&defaultValue);
		if (mtdReturnType->Equals("object"))
		{
			Node::Delete(methodNode);
			return;
		}

		document->CreateTextElementWithParentNS("returntype",mtdReturnType,methodNode);
		returnType = NULL;
		
		int parametersNode =document->CreateElementWithParentNS("parameters",methodNode);
		int paramNode=0;
	    int returnValuesNode =0 ; 

		for (int i=0 ;i < paramInfo->Length; i++)
		{
			defaultValue ="0";
			String *parameterName=paramInfo[i]->Name;
			String *dataType=ConvertType(paramInfo[i]->ParameterType,&defaultValue);  
			
			if (dataType->Equals("object"))
			{
				Node::Delete(methodNode);
				return;
			}
			paramNode = document->CreateElementWithParentNS("parameter",parametersNode);
			document->CreateTextElementWithParentNS("name",parameterName,paramNode);

			int dataypeNode = document->CreateTextElementWithParentNS("datatype",dataType,paramNode);
			//	If the method argument is ByRef Type
			//
			if ((paramInfo[i]->ParameterType)->IsByRef )
			{
				Node::SetAttribute(dataypeNode,"ct","ByRef");
				if (returnValuesNode == 0)
				{
					returnValuesNode =document->CreateElementWithParentNS("returnValues",methodNode);
				}
				int retNode =document->CreateTextElementWithParentNS("returnValue",parameterName,returnValuesNode);
				Node::SetAttribute(retNode,"dataType",dataType);
			}
			// Reads the default value for the arguments if exists 
			//
			if (paramInfo[i]->DefaultValue->ToString() != "" )
			{
				Node::SetAttribute(dataypeNode,"defaultValue",paramInfo[i]->DefaultValue->ToString());
			}
			else
			{
				Node::SetAttribute(dataypeNode,"defaultValue",defaultValue);
			}
	
		}
		defaultValue= NULL;
	}
	catch(Exception *ex)
	{
		throw ex;
	}
}


//          Convert The Type Name of  .NET Meta Data Types to
//    simple primitive Type name for the ease use of the front-end
String *MetaData::ConvertType(Type *dataType,String **defaultValue)
{
	TypeCode typeCode = Type::GetTypeCode(dataType);
	String *returnType =NULL;
	switch(typeCode)
	{
		case TypeCode::Int16:
		{
			returnType="i2";
			*defaultValue = "0";
			break;
		}
		case TypeCode::Int32:
		{
			returnType="i4";
			*defaultValue = "0";
			break;
		}
		case TypeCode::Int64:
		{
			returnType="i8";
			*defaultValue = "0";
			break;
		}
		case TypeCode::Double:
		{
			returnType="r8";
			*defaultValue = "0.0";
			break;
		}
		case TypeCode::Single:
		{
			returnType="r4";
			*defaultValue = "0.0";
			break;
		}
		case TypeCode::String:
		{
			returnType="string";
			*defaultValue = " ";
			break;
		}
		case TypeCode::Char:
		{
			returnType="char";
			*defaultValue = "a";
			break;
		}
		case TypeCode::Boolean:
		{
			returnType="boolean";
			*defaultValue = "true";
			break;
		}
		case TypeCode::Byte:
		{
			returnType="i1";
			*defaultValue = "0";
			break;
		}
		case TypeCode::SByte:
		{
			returnType="i1";
			*defaultValue = "0";
			break;
		}
		case TypeCode::DateTime:
		{
			*defaultValue = System::DateTime::Today.ToString();
			returnType="dateTime";
			break;
		}
		default:
		{
			String *dataTypeName= dataType->FullName;
		
			if (dataTypeName->Equals("System.Xml.XmlDocument"))
			{
				*defaultValue= "0";
				returnType="xml";
			}
			else if(dataTypeName->Equals("System.Void"))
			{
				returnType="void";
			}
			else if (dataTypeName->Equals("System.Xml.XmlDocument&"))
			{
				*defaultValue= "0";
				returnType="xml";
			}
			else if(dataTypeName->Equals("System.Int16&"))
			{
				returnType="i2";
			}
			else if(dataTypeName->Equals("System.Int32&"))
			{
				*defaultValue= "0";
				returnType="i4";
			}
			else if(dataTypeName->Equals("System.Int64&"))
			{
				*defaultValue= "0";
				returnType="i8";
			}
			else if(dataTypeName->Equals("System.Double&"))
			{
				*defaultValue= "0";
				returnType="r8";
			}
			else if(dataTypeName->Equals("System.Single&"))
			{
				*defaultValue= "0.0";
				returnType="r4";
			}
			else if(dataTypeName->Equals("System.String&"))
			{
				*defaultValue = " ";
				returnType="string";
			}
			else if(dataTypeName->Equals("System.Char&"))
			{
				*defaultValue = "a";
				returnType="char";
			}
			else if(dataTypeName->Equals("System.Boolean&"))
			{
				*defaultValue = "true";
				returnType="boolean";
			}
			else if(dataTypeName->Equals("System.Byte&"))
			{
				*defaultValue = "0";
				returnType="i1";
			}
			else if(dataTypeName->Equals("System.SByte&"))
			{
				*defaultValue = "0";
				returnType="i1";
			}
			else if(dataTypeName->Equals("System.DateTime&"))
			{
				*defaultValue = System::DateTime::Today.ToString();
				returnType="dateTime";
			}
			else
			{
				returnType="object";
			}
			break;
		}
	}
	return returnType;
}


//
// Load the Assembly
//
Assembly* MetaData::LoadAssembly(String *assemblyName)
{
	Assembly  * assembly = NULL;
	try
	{
		if (assemblyName->EndsWith(".dll"))
		{
			assembly	=	Assembly::LoadFrom(assemblyName);
		}
		else
		{
			assembly	=	Assembly::Load(assemblyName);
		}
		return assembly;
	}
	catch(Exception *ex)
	{
		throw ex;
		assembly=NULL;
	}
}

